package com.elextec.lease.manager.task;

import com.elextec.framework.utils.WzDateUtil;
import com.elextec.framework.utils.WzGPSUtil;
import com.elextec.persist.dao.mybatis.BizDeviceConfMapperExt;
import com.elextec.persist.dao.mybatis.BizDeviceTrackMapperExt;
import com.elextec.persist.dao.mybatis.BizDeviceUsageMapperExt;
import com.elextec.persist.model.mybatis.BizDeviceTrack;
import com.elextec.persist.model.mybatis.BizDeviceUsage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.*;

/**
 * 设备定时统计类
 * create by yangkun on 2018/07/19
 */
@Component
public class BizDeviceUsageTask {
    /** 日志. */
    private final Logger logger = LoggerFactory.getLogger(BizDeviceUsageTask.class);

    @Value("${localsetting.move-speed-max-threshold}")
    private Double moveSpeedMaxThreshold;

    @Value("${localsetting.move-speed-min-threshold}")
    private Double moveSpeedMinThreshold;

    @Autowired
    private BizDeviceConfMapperExt bizDeviceConfMapperExt;

    @Autowired
    private BizDeviceTrackMapperExt bizDeviceTrackMapperExt;

    @Autowired
    private BizDeviceUsageMapperExt bizDeviceUsageMapperExt;

    /**
     * 每天12点05分启动计算前一天的运行时间和运行距离.
     */
    @Scheduled(cron = "0 5 0 * * ?")
//    @Scheduled(cron = "*/5 * * * * ?")//五秒一次
    public void calculateRunTimeAndDistance() {
        long calTime = WzDateUtil.str2Dt(WzDateUtil.dt2Str(new Date(), WzDateUtil.DT_FMT_3) + "000500", WzDateUtil.DT_FMT_1).getTime();
        if (Math.abs(System.currentTimeMillis() - calTime) > 1000 * 60 * 5) {
            return;
        }
        // 获取所有设备号
        try {
            List<String> deviceIds = bizDeviceConfMapperExt.getDistinctDeviceId();
            for (int i = 0; i < deviceIds.size(); i++) {
                BizDeviceUsage bizDeviceUsage = new BizDeviceUsage();
                // 当前设备ID赋值
                bizDeviceUsage.setDeviceId(deviceIds.get(i));
                // 当前设备记录入库时间（当前日期的前一天0点0分0秒）赋值
                bizDeviceUsage.setRecTime(WzDateUtil.dayStart(WzDateUtil.afterNDays(new Date(), -1)));
                // 当前设备id
                String deviceId = deviceIds.get(i);
                // 当前时间的前一天起始时间（0点0分0秒）时间戳毫秒
                long startTime = WzDateUtil.dayStart(WzDateUtil.afterNDays(new Date(), -1)).getTime();
                // 当前时间的前一天结束时间（23点59分59秒）时间戳毫秒
                long endTime = WzDateUtil.dayEnd(WzDateUtil.afterNDays(new Date(), -1)).getTime();
                Map<String, Object> map = new HashMap<String, Object>();
                map.put("deviceId", deviceId);
                map.put("startTime", startTime);
                map.put("endTime", endTime);
                List<BizDeviceTrack> bizDeviceTrackOriList = bizDeviceTrackMapperExt.getTotalTimeAndDistance(map);
                // 去除行驶速度大于 120Km/h 的点
                if (null == moveSpeedMaxThreshold) {
                    moveSpeedMaxThreshold = 28.0;
                }
                List<BizDeviceTrack> bizDeviceTrackList = denoiseForDeviceUsage(bizDeviceTrackOriList, moveSpeedMaxThreshold);
                // 计算总时间和总距离
                long totalTimes = 0;
                long totalDistances = 0;
//                List<String> resultList = new ArrayList<String>();
//                String rr = null;
                for (int j = 0; j < bizDeviceTrackList.size() - 1; j++) {
                    double tmpDistance = WzGPSUtil.calcDistanceByM(bizDeviceTrackList.get(j).getLat(), bizDeviceTrackList.get(j).getLon(), bizDeviceTrackList.get(j + 1).getLat(), bizDeviceTrackList.get(j + 1).getLon());
                    long tmpTime = Math.abs(bizDeviceTrackList.get(j).getLocTime() - bizDeviceTrackList.get(j + 1).getLocTime());
                    // 行驶速度小于 1m/s 的
                    if (null == moveSpeedMinThreshold) {
                        moveSpeedMinThreshold = 1.0;
                    }
                    if (moveSpeedMinThreshold < tmpDistance * 1000 / tmpTime) {
                        totalDistances += tmpDistance;
                        totalTimes += tmpTime;
                    }
//                    rr = "设备[" + bizDeviceTrackList.get(i).getDeviceId() + "]，从"
//                            + WzDateUtil.dt2Str(new Date(bizDeviceTrackList.get(i).getLocTime()), WzDateUtil.DT_FMT_1)
//                            + " 到 "
//                            + WzDateUtil.dt2Str(new Date(bizDeviceTrackList.get(i + 1).getLocTime()), WzDateUtil.DT_FMT_1)
//                            + " ，由 "
//                            + bizDeviceTrackList.get(i).getLat() + "," + bizDeviceTrackList.get(i).getLon()
//                            + " 至 "
//                            + bizDeviceTrackList.get(i + 1).getLat() + "," + bizDeviceTrackList.get(i + 1).getLon()
//                            + " ，运行了： "
//                            + tmpDistance + " 米，"
//                            + " 耗时： "
//                            + tmpTime;
//                    resultList.add(rr);
                }
//                // 计算当前设备前一天的使用总时间
//                long totalTimes = this.getTotalTime(bizDeviceTrackList);
//                // 计算当前设备前一天的使用总距离
//                long totalDistances = this.getTotalDistance(bizDeviceTrackList);
                // 当前设备当天使用总时间（毫秒）
                bizDeviceUsage.setUseDuration(totalTimes);
                // 当前设备当天使用总距离（米）
                bizDeviceUsage.setUseDistance(totalDistances);
                bizDeviceUsageMapperExt.insert(bizDeviceUsage);
                logger.info("设备[" + deviceId + "]，行驶 " + totalDistances + " 米，耗时 " + totalTimes + " 毫秒");
            }
            logger.info("定时任务成功执行完成");

        } catch (Exception e) {
            logger.error("定时任务执行失败", e);
        }
    }

    /**
     * 计算车辆使用时长.
     * @param locs 定位信息
     * @return 总时长
     */
    private long getTotalTime(List<BizDeviceTrack> locs) {
        if (null == locs || 0 == locs.size()) {
            return 0;
        }
        // 总时长
        long totalTime = 0;
        for (int i = 0; i < locs.size(); i++) {
            if ((i + 1) >= (locs.size() - 1)) {
                // 最后一轮,计算总时间
                totalTime += locs.get(i).getLocTime() - locs.get(i + 1).getLocTime();
                break;
            } else {
                totalTime += locs.get(i).getLocTime() - locs.get(i + 1).getLocTime();
            }
        }
        return totalTime;
    }

    /**
     * 计算车辆使用距离.
     * @param locs 定位信息
     * @return 总距离
     */
    private long getTotalDistance(List<BizDeviceTrack> locs) {
        long totalDistance = 0;
        for (int i = 0; i < locs.size(); i++) {
            // 最后一轮计算
            if ((i + 1) >= (locs.size() - 1)) {
                // 第一经度
                double lon1 = locs.get(i).getLon();
                // 第一纬度
                double lat1 = locs.get(i).getLat();
                // 第二经度
                double lon2 = locs.get(i + 1).getLon();
                // 第二纬度
                double lat2 = locs.get(i + 1).getLat();
                // 计算两点之间的距离
                totalDistance += WzGPSUtil.calcDistanceByM(lon1, lat1, lon2, lat2);
                break;
            } else {
                // 第一经度
                double lon1 = locs.get(i).getLon();
                // 第一纬度
                double lat1 = locs.get(i).getLat();
                // 第二经度
                double lon2 = locs.get(i + 1).getLon();
                // 第二纬度
                double lat2 = locs.get(i + 1).getLat();
                // 计算两点之间的距离
                totalDistance += WzGPSUtil.calcDistanceByM(lon1, lat1, lon2, lat2);
            }
        }
        return totalDistance;
    }

    /**
     * 轨迹降噪.
     * @param locs 轨迹
     * @param speedThreshold 去噪速度阈值（米/秒）
     * @return 去噪后的轨迹
     */
    public static List<BizDeviceTrack> denoiseForDeviceUsage(List<BizDeviceTrack> locs, Double speedThreshold) {
        // 记录点小于5个则直接返回
        if (null == locs || 5 >= locs.size() || null == speedThreshold) {
            return locs;
        }
        // 确定首点
        int firstIdx = -1;
        double tmpDistance1 = 0;
        long tmpIntervalTime1 = 0;
        double tmpDistance2 = 0;
        long tmpIntervalTime2 = 0;
        // 连续三个点的速度均正常的情况，此三点第一个点为首点
        for (int i = 0; i < locs.size() - 2; i++) {
            tmpDistance1 = WzGPSUtil.calcDistanceByM(locs.get(i).getLat(),
                    locs.get(i).getLon(),
                    locs.get(i + 1).getLat(),
                    locs.get(i + 1).getLon());
            tmpIntervalTime1 = (locs.get(i + 1).getLocTime() - locs.get(i).getLocTime()) / 1000;
            tmpDistance2 = WzGPSUtil.calcDistanceByM(locs.get(i + 1).getLat(),
                    locs.get(i + 1).getLon(),
                    locs.get(i + 2).getLat(),
                    locs.get(i + 2).getLon());
            tmpIntervalTime2 = (locs.get(i + 2).getLocTime() - locs.get(i + 1).getLocTime()) / 1000;
            if (speedThreshold >= (tmpDistance1 / tmpIntervalTime1)
                    && speedThreshold >= (tmpDistance2 / tmpIntervalTime2)) {
                firstIdx = i;
                break;
            }
        }
        // 如果到最后也没有找到首点，则直接返回全部点
        if (-1 == firstIdx) {
            return locs;
        }
        // 从首点开始对其后续点进行去噪
        List<BizDeviceTrack> usableLocs = new ArrayList<BizDeviceTrack>();
        for (int i = firstIdx; i < locs.size(); i++) {
            if (i == firstIdx) {
                usableLocs.add(locs.get(i));
            } else {
                tmpDistance1 = WzGPSUtil.calcDistanceByM(locs.get(i).getLat(),
                        locs.get(i).getLon(),
                        locs.get(i - 1).getLat(),
                        locs.get(i - 1).getLon());
                tmpIntervalTime1 = (locs.get(i).getLocTime() - locs.get(i - 1).getLocTime()) / 1000;
                if (speedThreshold >= (tmpDistance1 / tmpIntervalTime1)) {
                    usableLocs.add(locs.get(i));
                }
            }
        }
        return usableLocs;
    }
}
